package it.polito.connectedcar.message;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;

import it.polito.connectedcar.tcc.zone.Token;

/**
 * This is the counterpart of {@link IncomingMessage} and by extending @{link
 * {@link Message} it inheriths the common attributes. It is used to model a
 * message that must be sent over the network to a destination. It's important
 * to define an actionCode meaningful for the type of message in order to be
 * understood by the receiver. All subclasses must declare a sourceTypeCode to
 * be used to identify the kind of Entity sending the message.
 * 
 * @author Nicola Aresta
 * 
 */
public abstract class OutgoingMessage extends Message {

	public OutgoingMessage(byte sourceTypeCode, byte messageCode) {
		super(sourceTypeCode, messageCode);
		recipientsAddresses = new ArrayList<InetSocketAddress>();
	}

	private List<InetSocketAddress> recipientsAddresses;

	/**
	 * called to actually write into the storage the content of the message.
	 * Some implementations could use the forTCC flag to fill differently the
	 * message depending on the fact that the receiver is a TCC , which is a
	 * special entity in the system. Optionally even the token used to sign the
	 * message can be used to perform some operations.
	 * 
	 * @param st
	 * @param forTCC
	 * @param t
	 *            the token used to identify the group to which the receiver
	 *            belong.
	 */
	protected abstract void convertToData(Storage st, boolean forTCC, Token t);

	/**
	 * 
	 * This is called to safely write what will be read by message processors :
	 * the Timestamp and the codes needed for a meaningful messages exchange
	 * 
	 * @param t
	 * @param forTCC
	 */
	private void finalizeToStorage(Token t, boolean forTCC, Storage payload) {
		storage.reset();

		storage.writeLong(timestamp);
		storage.writeByte(sourceTypeCode << 4 | actionCode);
		convertToData(payload, forTCC, t);
	}

	/**
	 * This is the first entry point for external classes needing the byte array
	 * representation of the message to be written on network packets. This
	 * method will invoke subclasses methods to fill the storage, compute the
	 * payload size , header size and finally convert all of them in byte array.
	 * 
	 * 
	 * @param forTCC
	 * @param t
	 * @return
	 */
	public byte[] getMessageAsBytes(boolean forTCC, Token t) {
		// FIXME calculate the digest
		Storage payload = new Storage();
		finalizeToStorage(t, forTCC, payload);

		storage.writeInt(payload.size());

		for (Byte byte_ : payload.getStorageList())
			storage.writeByte(byte_);

		List<Byte> bytesList = storage.getStorageList();

		byte[] bytes = new byte[bytesList.size()];

		for (int i = 0; i < bytesList.size(); i++) {
			bytes[i] = bytesList.get(i).byteValue();
		}
		return bytes;
	}

	/**
	 * The Address to which the message will be sent
	 * 
	 * @return
	 */
	public List<InetSocketAddress> getRecipientsAddresses() {
		return recipientsAddresses;
	}

	public void setRecipientsAddresses(List<InetSocketAddress> recipientsAddresses) {
		this.recipientsAddresses = recipientsAddresses;
	}
}
